/** @file    LogContext.cpp
 *  @author  Alessandro Polo
 *  @version $Id: LogContext.cpp 2634 2010-06-09 23:01:02Z alex $
 *  @brief
 * File containing methods for the wosh::LogContext class.
 * The header for this class can be found in LogContext.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <core/LogContext.h>

 #include <core/ObjectAllocator.h>
 #include <core/DataModels.h>
 #include <core/LogHandlers.h>
 #include <stdarg.h> // va_list: cos of log()

using namespace std;
namespace wosh {

// Meyer singleton! static reference is allocated on first user
// not thread safe
extern "C" {
	ListT<LogContext*>& LogContext::getInstances() {
		static ListT<LogContext*> instances;
		return instances;
	 }
};

 WOSH_REGISTER(wosh::LogContext, "", 0.01, _static_LogContext )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

LogContext::LogContext()
	: indent(0), minLevel(LOG_ALL), printContext(true), handler(NULL) { 
	memset( this->context,'\0',_LogContext_CONTEXT_MAX_SIZE);
	LogContext::getInstances().transactionBeginWrite();
	LogContext::getInstances().push_back(this);
	this->handler = DefaultSharedLogHandler::getInstance();
	LogContext::getInstances().transactionEnd();
 }

LogContext::~LogContext() {
	LogContext::getInstances().transactionBeginWrite();
	LogContext::getInstances().pop(this);
	LogContext::getInstances().transactionEnd();
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// LOGGING

void LogContext::operator() ( LogLevel level, const char* format, ... ) {
	this->configMux.lockForRead();
	if( this->minLevel < level || this->handler == NULL ) {
		this->configMux.unLock();
		return;
	 }
	va_list arglist;
	va_start( arglist, format );
	this->handler->log( this->context, level, format, arglist );
	va_end( arglist );
	this->configMux.unLock();
 }

void LogContext::verbose( const char* format, ... ) {
	this->configMux.lockForRead();
	if( this->minLevel < LOG_VERBOSE || this->handler == NULL ) {
		this->configMux.unLock(); return;
	 }
	va_list arglist; va_start( arglist, format );
	this->handler->log( this->context, LOG_VERBOSE, format, arglist );
	va_end( arglist );
	this->configMux.unLock();
 }

void LogContext::debug( const char* format, ... ) {
	this->configMux.lockForRead();
	if( this->minLevel < LOG_DEBUG || this->handler == NULL ) {
		this->configMux.unLock(); return;
	 }
	va_list arglist; va_start( arglist, format );
	this->handler->log( this->context, LOG_DEBUG, format, arglist );
	va_end( arglist );
	this->configMux.unLock();
 }

void LogContext::info( const char* format, ... ) {
	this->configMux.lockForRead();
	if( this->minLevel < LOG_INFO || this->handler == NULL ) {
		this->configMux.unLock(); return;
	 }
	va_list arglist; va_start( arglist, format );
	this->handler->log( this->context, LOG_INFO, format, arglist );
	va_end( arglist );
	this->configMux.unLock();
 }

void LogContext::warn( const char* format, ... ) {
	this->configMux.lockForRead();
	if( this->minLevel < LOG_WARNING || this->handler == NULL ) {
		this->configMux.unLock(); return;
	 }
	va_list arglist; va_start( arglist, format );
	this->handler->log( this->context, LOG_WARNING, format, arglist );
	va_end( arglist );
	this->configMux.unLock();
 }

void LogContext::critical( const char* format, ... ) {
	this->configMux.lockForRead();
	if( this->minLevel < LOG_CRITICAL || this->handler == NULL ) {
		this->configMux.unLock(); return;
	 }
	va_list arglist; va_start( arglist, format );
	this->handler->log( this->context, LOG_CRITICAL, format, arglist );
	va_end( arglist );
	this->configMux.unLock();
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// LOGGING
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// SETTERS

void LogContext::setHandler( LogHandler* new_handler ) {
	MutexLockerWrite mL(&this->configMux);
	this->handler = new_handler;
 }
void LogContext::setLevel( LogLevel min_level ) {
	MutexLockerWrite mL(&this->configMux);
	if ( min_level < LOG_UNKNOWN || min_level > LOG_ALL )
		this->minLevel = LOG_UNKNOWN;
	else
		this->minLevel = min_level;
 }

void LogContext::setContext( const std::string& name, bool do_print_context ) {
	MutexLockerWrite mL(&this->configMux);
	this->contextShadow = name;
	this->printContext = do_print_context;
	rebuiltContext();
 }
void LogContext::setIndent( short spaces ) {
	MutexLockerWrite mL(&this->configMux);
	this->indent = spaces;
	rebuiltContext();
 }
void LogContext::setContextPrint( bool enabled ) {
	MutexLockerWrite mL(&this->configMux);
	this->printContext = enabled;
	rebuiltContext();
 }

void LogContext::rebuiltContext() {
	if ( !this->printContext ) {
		memset( this->context, 0, _LogContext_CONTEXT_MAX_SIZE );
		return;
	 }
	std::string temp = this->contextShadow + ":";
	memset( this->context, ' ', this->indent );
	strncpy( this->context+this->indent, temp.data(), _LogContext_CONTEXT_MAX_SIZE-1-this->indent );
 }

long LogContext::getHandlerID() const {
	MutexLockerRead mL(&this->configMux);
	if (this->handler == NULL) return 0;
	return this->handler->getID();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

DataFieldCollector<LogContext>* LogContext::getDataFields() {
	DataFieldCollector<LogContext>* fields = new DataFieldCollector<LogContext>();
	fields->add<const std::string&, Variant>(&LogContext::getContext, "Name", 0);
	fields->add<const char*,Variant>(&LogContext::getClassName, "Type", 1);
	fields->add<const char*,Variant>(&LogContext::getLogLevelAsString, "LogLevel[Str]", 2);
	fields->add<long,Variant>(&LogContext::getHandlerID, "HandlerID", 3);
	fields->add<short,Variant>(&LogContext::getIndent, "Indent", 4);
	fields->add<LogLevel,Variant>(&LogContext::getLevel, "LogLevel", 5, false);
	return fields;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

const char* LogLevelStr[] = {
	"UNKNOWN",	// (LogLevel)0
	"NONE",		// (LogLevel)1
	"CRITICAL",	// (LogLevel)2
	"WARNING",	// (LogLevel)3
	"INFO",		// (LogLevel)4
	"VERBOSE",	// (LogLevel)5
	"DEBUG",	// (LogLevel)6
	"ALL"		// (LogLevel)7
 };

Enum2StrFromArray(LogContext::getLogLevelAsString, LogLevel, LOG_UNKNOWN, LOG_ALL, 0, LogLevelStr)

Str2EnumByArray(LogContext::getLogLevelFromString, LogLevel, 0, LOG_ALL, LOG_UNKNOWN, LogLevelStr)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void log( LogLevel level, const char* context, const char* format, ... ) {
	va_list arglist;
	va_start( arglist, format );
	DefaultSharedLogHandler::getInstance()->log( context, level, format, arglist );
	va_end( arglist );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
